package com.abou.model.common.product;

import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;
import org.hibernate.annotations.CollectionOfElements;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import com.abou.constant.product.ECategory;
import com.abou.constant.product.EClotheType;
import com.abou.constant.product.EColor;
import com.abou.constant.product.EProductType;
import com.abou.constant.product.ESize;
import com.abou.model.common.Entity;
import com.abou.mvc.validation.annotation.DoubleValid;
import com.abou.mvc.validation.annotation.ProductColorValid;
import com.abou.mvc.validation.annotation.ProductMultipartValid;

/**
 * The class of a product.
 * 
 * @author khalid
 */
@javax.persistence.Entity
@NamedQueries( {
    @NamedQuery(name = "Product.findAllProductsAsc", query = "select p from Product p order by p.price asc"),
    @NamedQuery(name = "Product.findProductsAsc", query = "select p from Product p where p.name LIKE :name "
        + "AND p.description LIKE :description AND p.productType LIKE :productType "
        + "order by p.price asc"),
    @NamedQuery(name = "Product.findProductsByNameDescriptionAsc", query = "select p from Product p where p.name LIKE :name "
        + "AND p.description LIKE :description order by p.price asc"),
    @NamedQuery(name = "Product.findProductsForClothesAsc", query = "select p from Product p where p.name "
        + "LIKE :name "
        + "AND p.description LIKE :description "
        + "AND p.productType = :productType AND p.clotheType = :clotheType order by p.price asc"),
    @NamedQuery(name = "Product.findProductsForClothesNoClotheTypeAsc", query = "select p from Product p where "
        + "p.name LIKE :name "
        + "AND p.description LIKE :description "
        + "AND p.productType = :productType order by p.price asc"),
    @NamedQuery(name = "Product.findProductsForClothesNoClotheTypeDesc", query = "select p from Product p where "
        + "p.name LIKE :name "
        + "AND p.description LIKE :description AND p.category = :category "
        + "AND p.productType = :productType order by p.price desc"),
    @NamedQuery(name = "Product.findProductsForClothesDesc", query = "select p from Product p where p.name "
        + "LIKE :name "
        + "AND p.description LIKE :description "
        + "AND p.productType = :productType AND p.clotheType = :clotheType order by p.price desc"),
    @NamedQuery(name = "Product.findProductsByNameDescriptionDesc", query = "select p from Product p where"
        + " p.name LIKE :name "
        + "AND p.description LIKE :description order by p.price desc"),
    @NamedQuery(name = "Product.findProductByNameAsc", query = "select p from Product p where p.name LIKE :name "
        + "order by p.price asc"),
    @NamedQuery(name = "Product.findProductByDescriptionAsc", query = "select p from Product p where "
        + "p.description LIKE :description order by p.price asc"),
    @NamedQuery(name = "Product.findProductByTypeAsc", query = "select p from Product p where "
        + "p.productType = :productType order by p.price asc"),
    @NamedQuery(name = "Product.findAllProductsDesc", query = "select p from Product p order by p.price desc"),
    @NamedQuery(name = "Product.findProductsDesc", query = "select p from Product p where p.name LIKE :name "
        + "AND p.description LIKE :description AND p.productType = :productType "
        + "order by p.price desc"),
    @NamedQuery(name = "Product.findProductByNameDesc", query = "select p from Product p where p.name "
        + "LIKE :name order by p.price desc"),
    @NamedQuery(name = "Product.findProductByDescriptionDesc", query = "select p from Product p where "
        + "p.description LIKE :description order by p.price desc"),
    @NamedQuery(name = "Product.findProductByTypeDesc", query = "select p from Product p where "
        + "p.productType = :productType order by p.price desc") })
@ProductMultipartValid
@ProductColorValid
public class Product extends Entity {
  
  private static final long serialVersionUID = 1L;
  
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private int id;
  /**
   * The product image 1 (not persisted in DB but saved in repository)
   */
  @Transient
  private CommonsMultipartFile image1;
  private String imageRealPath1;
  private String imagePath1;
  
  /**
   * The product image 2 (not persisted in DB but saved in repository)
   */
  @Transient
  private CommonsMultipartFile image2;
  private String imageRealPath2;
  private String imagePath2;
  
  /**
   * The product image 3 (not persisted in DB but saved in repository)
   */
  @Transient
  private CommonsMultipartFile image3;
  private String imageRealPath3;
  private String imagePath3;
  
  /**
   * The product image 4 (not persisted in DB but saved in repository)
   */
  @Transient
  private CommonsMultipartFile image4;
  private String imageRealPath4;
  private String imagePath4;
  
  @NotEmpty
  @NotNull
  private String name;
  @NotEmpty
  @NotNull
  private String description;
  @NotEmpty
  @NotNull
  @DoubleValid
  private String price;
  @Enumerated(value = EnumType.STRING)
  private EProductType productType;
  private Boolean inStock = true;
  private Boolean inPromo = false;
  private Boolean newness = true;
  private Sale productSale;
  
  /**
   * The default constructor
   */
  public Product() {

    super();
  }
  
  /**
   * @param id
   * @param image1
   * @param image2
   * @param image3
   * @param image4
   * @param name
   * @param description
   * @param price
   * @param productType
   */
  public Product(int id, CommonsMultipartFile image1,
      CommonsMultipartFile image2, CommonsMultipartFile image3,
      CommonsMultipartFile image4, String name, String description,
      String price, EProductType productType) {

    super();
    this.id = id;
    this.image1 = image1;
    this.image2 = image2;
    this.image3 = image3;
    this.image4 = image4;
    this.name = name;
    this.description = description;
    this.price = price;
    this.productType = productType;
  }
  
  /**
   * @param id The product id
   */
  public Product(int id) {

    super();
    this.id = id;
  }
  
  /**
   * @return Whether the product is clothe type or not.
   */
  public boolean isClothe() {

    return getProductType() != EProductType.BODY_CARE
        && getProductType() != EProductType.BOOK
        && getProductType() != EProductType.ACCESSORIES;
  }
  
  /**
   * @return Whether the product is in sale
   */
  public boolean isInSale() {

    return productSale != null;
  }
  
  /**
   * Sets the product name
   * 
   * @param name The product name
   */
  public void setName(String name) {

    this.name = name;
  }
  
  /**
   * @return The product name
   */
  public String getName() {

    return name;
  }
  
  /**
   * Sets the product description
   * 
   * @param description the product description
   */
  public void setDescription(String description) {

    this.description = description;
  }
  
  /**
   * @return The product description
   */
  public String getDescription() {

    return description;
  }
  
  /**
   * Sets the product price
   * 
   * @param price The product price
   */
  public void setPrice(String price) {

    this.price = price;
  }
  
  /**
   * @return The product price
   */
  public String getPrice() {

    return price;
  }
  
  /**
   * Sets the product productType
   * 
   * @param productType The product productType
   */
  public void setProductType(EProductType productType) {

    this.productType = productType;
  }
  
  /**
   * @param color The product colors list
   */
  public void setColors(List<EColor> color) {

    this.colors = color;
  }
  
  /**
   * @return The product color
   */
  public List<EColor> getColors() {

    return colors;
  }
  
  /**
   * @param inStock The new value
   */
  public void setInStock(Boolean inStock) {

    this.inStock = inStock;
  }
  
  /**
   * @return Whether the product is in stock or not
   */
  public Boolean getInStock() {

    return inStock;
  }
  
  /**
   * @return The product productType
   */
  public EProductType getProductType() {

    return productType;
  }
  
  /**
   * Sets the product ID
   * 
   * @param id The product ID
   */
  public void setId(int id) {

    this.id = id;
  }
  
  /**
   * @return The product ID
   */
  public int getId() {

    return id;
  }
  
  public CommonsMultipartFile getImage1() {

    return image1;
  }
  
  public void setImage1(CommonsMultipartFile image1) {

    this.image1 = image1;
  }
  
  public String getImageRealPath1() {

    return imageRealPath1;
  }
  
  public void setImageRealPath1(String imageRealPath1) {

    this.imageRealPath1 = imageRealPath1;
  }
  
  public String getImagePath1() {

    return imagePath1;
  }
  
  public void setImagePath1(String imagePath1) {

    this.imagePath1 = imagePath1;
  }
  
  public CommonsMultipartFile getImage2() {

    return image2;
  }
  
  public void setImage2(CommonsMultipartFile image2) {

    this.image2 = image2;
  }
  
  public String getImageRealPath2() {

    return imageRealPath2;
  }
  
  public void setImageRealPath2(String imageRealPath2) {

    this.imageRealPath2 = imageRealPath2;
  }
  
  public String getImagePath2() {

    return imagePath2;
  }
  
  public void setImagePath2(String imagePath2) {

    this.imagePath2 = imagePath2;
  }
  
  public CommonsMultipartFile getImage3() {

    return image3;
  }
  
  public void setImage3(CommonsMultipartFile image3) {

    this.image3 = image3;
  }
  
  public String getImageRealPath3() {

    return imageRealPath3;
  }
  
  public void setImageRealPath3(String imageRealPath3) {

    this.imageRealPath3 = imageRealPath3;
  }
  
  public String getImagePath3() {

    return imagePath3;
  }
  
  public void setImagePath3(String imagePath3) {

    this.imagePath3 = imagePath3;
  }
  
  public CommonsMultipartFile getImage4() {

    return image4;
  }
  
  public void setImage4(CommonsMultipartFile image4) {

    this.image4 = image4;
  }
  
  public String getImageRealPath4() {

    return imageRealPath4;
  }
  
  public void setImageRealPath4(String imageRealPath4) {

    this.imageRealPath4 = imageRealPath4;
  }
  
  public String getImagePath4() {

    return imagePath4;
  }
  
  public void setImagePath4(String imagePath4) {

    this.imagePath4 = imagePath4;
  }
  
  /**
   * @param inPromo The new value for is in promo
   */
  public void setInPromo(Boolean inPromo) {

    this.inPromo = inPromo;
  }
  
  /**
   * @return Whether the product is in promo
   */
  public Boolean getInPromo() {

    return inPromo;
  }
  
  /**
   * @param newness The new value
   */
  public void setNewness(Boolean newness) {

    this.newness = newness;
  }
  
  /**
   * @return Whether the product is a newness
   */
  public Boolean getNewness() {

    return newness;
  }
  
  /********************************** only if its a clothing productType *************************************/
  
  @Enumerated(value = EnumType.STRING)
  private ECategory category;
  @CollectionOfElements(fetch = FetchType.EAGER)
  @Fetch(FetchMode.SUBSELECT)
  @JoinTable(name = "product_sizes", joinColumns = { @JoinColumn(name = "product_id") })
  @Enumerated(value = EnumType.STRING)
  @Column(name = "sizes")
  private List<ESize> sizes;
  @CollectionOfElements(fetch = FetchType.EAGER)
  @Fetch(FetchMode.SUBSELECT)
  @JoinTable(name = "product_colors", joinColumns = { @JoinColumn(name = "product_id") })
  @Enumerated(value = EnumType.STRING)
  @Column(name = "colors")
  private List<EColor> colors = new ArrayList<EColor>();
  private String makeup;
  @Enumerated(value = EnumType.STRING)
  private EClotheType clotheType;
  
  /**
   * @param id
   * @param image1
   * @param image2
   * @param image3
   * @param image4
   * @param name
   * @param description
   * @param price
   * @param productType
   * @param clotheType
   * @param category
   * @param size
   * @param color
   */
  public Product(int id, CommonsMultipartFile image1,
      CommonsMultipartFile image2, CommonsMultipartFile image3,
      CommonsMultipartFile image4, String name, String description,
      String price, EProductType productType, EClotheType clotheType,
      ECategory category, List<ESize> size, List<EColor> color, String makeup) {

    super();
    this.id = id;
    this.image1 = image1;
    this.image2 = image2;
    this.image3 = image3;
    this.image4 = image4;
    this.name = name;
    this.description = description;
    this.price = price;
    this.productType = productType;
    this.clotheType = clotheType;
    this.category = category;
    this.sizes = size;
    this.colors = color;
    this.makeup = makeup;
  }
  
  /**
   * Sets the size
   * 
   * @param sizes The products sizes list
   */
  public void setSizes(List<ESize> sizes) {

    this.sizes = sizes;
  }
  
  /**
   * @return The products sizes list
   */
  public List<ESize> getSizes() {

    return sizes;
  }
  
  /**
   * Sets the category
   * 
   * @param category The category
   */
  public void setCategory(ECategory category) {

    this.category = category;
  }
  
  /**
   * @return The category
   */
  public ECategory getCategory() {

    return category;
  }
  
  /**
   * Sets the clothe productType.
   * 
   * @param clotheType The clothe productType
   */
  public void setClotheType(EClotheType clotheType) {

    this.clotheType = clotheType;
  }
  
  /**
   * @return The clothe productType
   */
  public EClotheType getClotheType() {

    return clotheType;
  }
  
  /**
   * @param productSale The product sale new value.
   */
  public void setProductSale(Sale productSale) {

    this.productSale = productSale;
  }
  
  /**
   * @return The product sale
   */
  public Sale getProductSale() {

    return productSale;
  }
  
  public void setMakeup(String makeup) {

    this.makeup = makeup;
  }
  
  public String getMakeup() {

    return makeup;
  }
  
  /********************************************************************/
}